package _general;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.DecimalFormat;
import java.text.NumberFormat;

public class PerformanceTest {
	
	//private static List<SortAlgorithm> sortAlgorithm = new ArrayList<SortAlgorithm>();
	// [Algorithm][presortion][number of Elements][Iteration]
	private static long[][][][] resultSet;
	private static int numberTestcases;
	private static int countedTestcases = 0;
	
	public static void main(String[] args) throws Exception {
		
		// prepare everything
		NumberFormat formatter = new DecimalFormat("#,###,###");

		// The different testcases are ordered by the used algorithm, so you need a mechanism to
		// make sure, that the same iteration at a different algorithm uses the same element list 
		resultSet = new long[Config.algorithm.length]
		                    [Config.preSortTypes.length]
		                    [Config.listSize.length]
		                    [Config.iterations];

		// To control, that every iteration uses the same element list, a list of seed values will
		// be created, if a random presortion is used.
		boolean hasRandom = false;
		long[] randomSeed = null;
		
		for(int i = 0; i < Config.preSortTypes.length; i++)
			if (Config.preSortTypes[i].toLowerCase().equals("random")) hasRandom = true;
		
		// if random is chosen, save every random seed in an array
		if (hasRandom) {
			randomSeed = new long[Config.iterations];
			for (int i = 0; i < Config.iterations; i++){
				randomSeed[i] = (long) (Math.random() * Long.MAX_VALUE);
			}
		}
		
		numberTestcases = Config.algorithm.length * Config.preSortTypes.length * Config.listSize.length * Config.iterations;
		
		
		if (Config.count) System.out.println("number of Testcases: " + numberTestcases);
		
		// now we can start to construct the testcases
		for(int i = 0; i < Config.algorithm.length; i++) {
			
			for (int j = 0; j < Config.preSortTypes.length; j++) {
				
				for (int k = 0; k < Config.listSize.length; k++){
					for (int l = 0; l < Config.iterations; l++) {
						
						countedTestcases++;
					
						try {
							if (Config.count && countedTestcases % 100 == 0) System.out.println(countedTestcases + " testcases proceeded");
							// create a new instance of the chosen algorithm
							TestInterface testcase = (TestInterface) newInstance("./", Config.algorithm[i]);
							
							if (Config.log) {
								System.out.println("Create new Testcase:");
								System.out.println("Algorithmus:\t\t" + testcase.getName());
								System.out.println("preSort:\t\t" + Config.preSortTypes[j]);
								System.out.println("Number of Elements:\t" + formatter.format(Config.listSize[k]));
								System.out.println("Iteration:\t\t" + (l+1));
								System.out.flush();
							}
							
							// if the testcase uses random elements, you have to use a different method
							if (Config.preSortTypes[j].toLowerCase().equals("random")) {
								testcase.createInitialList(Config.preSortTypes[j], Config.listSize[k], randomSeed[l]);
							} else {
								testcase.createInitialList(Config.preSortTypes[j], Config.listSize[k]);
							}
							
							// start the test and measure the elapsed time
							long startTime = System.nanoTime();
							testcase.start();
							resultSet[i][j][k][l] = System.nanoTime() - startTime;
							
							
							if (Config.showResult) testcase.showResult();
							
							if (Config.log) {
								System.out.println("Used Time: \t\t" + (double) ((double)resultSet[i][j][k][l] / (double)1000000) + " ms");
								System.out.println("-------------------------------------------------------------");
								System.out.flush();
							}
							
							testcase.destroy();
							testcase = null;
							
							System.gc();

							
						} catch (Exception e) {
							System.out.println("Performance Test failed!!!");
							System.out.println("-------------------------------------------------------------");
							e.printStackTrace();
							System.out.flush();
						}
					}
				}
			}
		}
		
		if (Config.problemToWindow) {
			TestInterface testcase = (TestInterface) newInstance("./", Config.algorithm[0]);
			testcase.createInitialList(Config.preSortTypes[0], Config.listSize[0], randomSeed[0]);
			testcase.problemToWindow();
		}
		
		if (Config.problemToConsole) {
			TestInterface testcase = (TestInterface) newInstance("./", Config.algorithm[0]);
			testcase.createInitialList(Config.preSortTypes[0], Config.listSize[0], randomSeed[0]);
			testcase.problemToConsole();
		}
		
		if (Config.analysisToWindow) {
				
		}
		
		if (Config.analysisToConsole)
			for(int i = 0; i < Config.algorithm.length; i++) {
				
				TestInterface testcase = (TestInterface) newInstance("./", Config.algorithm[i]);
				
				System.out.println("Algorithmus: " + testcase.getName());
				for (int j = 0; j < Config.preSortTypes.length; j++) {
					
					for (int k = 0; k < Config.listSize.length; k++){
						long sum = 0;
						
						for (int l = 0; l < Config.iterations; l++) {
							sum += resultSet[i][j][k][l];
						}
						
						System.out.println(Config.listSize[k] + " Elements:\t" + (sum/Config.iterations)/1000000 + " ms in average\n");
					}
				}
			}
	
		if (Config.analysisToFile)
			try{
				for(int i = 0; i < Config.algorithm.length; i++) {
					for (int j = 0; j < Config.preSortTypes.length; j++) {
						File outputFile = new File(Config.preSortTypes[j] + "_" + Config.algorithm[i] + ".dat");
						FileWriter fw = new FileWriter(outputFile);
						BufferedWriter bw = new BufferedWriter(fw);
					
						for (int k = 0; k < Config.listSize.length; k++){
							int sum = 0;
						
							for (int l = 0; l < Config.iterations; l++) {
								sum += resultSet[i][j][k][l];
							}
						
							bw.write(Config.listSize[k] + "  " + (sum/Config.iterations) + "\n");
						}
					
						bw.close();
					}
				}
			}catch (Exception e) {
				e.printStackTrace();
			}
		
	}
	
	static Object newInstance( String path, String classname ) throws Exception 
	  { 
	    URL url = new File( path ).toURI().toURL(); 
	    URLClassLoader cl = new URLClassLoader( new URL[]{ url } ); 
	    Class<?> c = cl.loadClass( classname ); 
	    return c.newInstance(); 
	  }

}
